# gozerbot/callbacks.py
#
#

""" implements callbacks """

__copyright__ = 'this file is in the public domain'

from gozerbot.generic import rlog, handle_exception, calledfrom
from gozerbot.ircevent import makeargrest
from gozerbot.dol import Dol
import gozerbot.thr as thr
import sys, copy

class Callback(object):

    """ class representing a callback """

    def __init__(self, func, prereq, plugname, kwargs, threaded=True):
        self.func = func
        self.prereq = prereq
        self.plugname = plugname
        self.kwargs = kwargs
        self.threaded = copy.deepcopy(threaded)

class Callbacks(Dol):

    """ dict of lists containing callbacks """

    def size(self):
        """ return nr of callbacks """
        return len(self)

    def add(self, what, func, prereq, kwargs=None, threaded=True):
        """ add a callback """
        what = what.upper()
        # get the plugin this callback was registered from
        plugname = calledfrom(sys._getframe())
        # see if kwargs is set if not init to {}
        if not kwargs:
            kwargs = {}
        # add callback to the dict of lists
        Dol.add(self, what, Callback(func, prereq, plugname, kwargs, threaded))
        rlog(-3, 'callbacks', 'added %s (%s)' % (what, plugname))

    def unload(self, plug):
        """ unload plugin """
        unload = []
        # look for all callbacks in a plugin
        for name, cblist in self.iteritems():
            index = 0
            for item in cblist:
                if item.plugname == plug:
                    unload.append((name, index))
                index += 1
        # delete callbacks
        for callback in unload[::-1]:
            self.delete(callback[0], callback[1])
            rlog(1, 'callbacks', 'unloaded %s' % callback[0])

    def whereis(self, what):
        """ show where callback is registerd """
        result = []
        what = what.upper()
        for name, callback in self.iteritems():
            if name == what:
                for item in callback:
                    if not item.plugname in result:
                        result.append(item.plugname)
        return result

class Botcallbacks(Callbacks):

    """ callback on ircevent """

    def check(self, bot, ievent):
        """ check for callbacks to be fired """
        # check for "ALL" callbacks
        if self.has_key('ALL'):
            for cb in self['ALL']:
                self.callback(cb, bot, ievent)
        cmnd = ievent.cmnd.upper()
        # check for CMND callbacks
        if self.has_key(cmnd):
            for cb in self[cmnd]:
                self.callback(cb, bot, ievent)

    def callback(self, cb, bot, ievent):
        # callback cb 
        try:
            # set ievent bot and socket
            ievent.bot = bot
            ievent.sock = bot.sock
            makeargrest(ievent)
            # see if the callback pre requirement succeeds
            if cb.prereq:
                rlog(-10, 'callback', 'excecuting in loop %s' % \
str(cb.prereq))
                if not cb.prereq(bot, ievent):
                    return
            # check if callback function is there
            if not cb.func:
                return
            # start callback in its own thread
            rlog(-10, 'callback', 'excecuting callback %s' % \
str(cb.func))
            if cb.threaded:
                thr.start_new_thread(cb.func, (bot, ievent), cb.kwargs)
            else:
                cb.func(bot, ievent, **cb.kwargs)
        except Exception, ex:
            handle_exception()

class Jabbercallbacks(Callbacks):

    """ callback on stanza.getName() """

    def check(self, bot, stanza):
        """ check for callbacks to be fired """
        # check for "ALL" callbacks
        if self.has_key('ALL'):
            for cb in self['ALL']:
                self.callback(cb, bot, stanza)
        cmnd = stanza.getName()
        # check for CMND callbacks
        if self.has_key(cmnd):
            for cb in self[cmnd]:
                self.callback(cb, bot, stanza)

    def callback(self, cb, bot, stanza):
        # callback cb 
        try:
            # see if the callback pre requirement succeeds
            if cb.prereq:
                rlog(-10, 'callback', 'excecuting in loop %s' % \
str(cb.prereq))
                if not cb.prereq(bot, stanza):
                    return
            # check if callback function is there
            if not cb.func:
                return
            # start callback in its own thread
            rlog(-10, 'callback', 'excecuting callback %s' % \
str(cb.func))
            if cb.threaded:
                thr.start_new_thread(cb.func, (bot, stanza), cb.kwargs)
            else:
                cb.func(bot, stanza, **cb.kwargs)
        except Exception, ex:
            handle_exception()

callbacks = Jabbercallbacks()
